package orm

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

type SimpleModel struct {
	db DataBaseOperator
}

func Simple(db DataBaseOperator) *SimpleModel {
	return &SimpleModel{db: db}
}

func (m *SimpleModel) Insert(st SimpleTableDefiner) error {
	if reflect.TypeOf(st).Kind() != reflect.Ptr {
		panic("SimpleModel.New 接收的Table接口必须是指针类型")
	}
	fp, ok := st.(TableFieldInfoProvider)
	if !ok {
		fp = ReflectFieldInfo{}.New(st)
	}
	fs := fp.Fields()
	pk := st.PrimaryKey()
	nfs := make([]string, 0)
	for _, f := range fs {
		if f != pk {
			nfs = append(nfs, f)
		}
	}
	fs = nfs
	if len(fs) == 0 {
		return errors.New("要插入的数据行数为空")
	}
	cs := make([]string, 0)
	for _, f := range fs {
		cs = append(cs, fp.ColumnName(f))
	}
	query := "INSERT INTO `" + st.TableName() + "`(" + strings.Join(cs, ", ") + ") VALUES (?" + strings.Repeat(", ?", len(fs)-1) + ")"
	argv := make([]interface{}, 0)
	for _, f := range fs {
		argv = append(argv, fp.GetFieldValue(f))
	}
	fmt.Println(query, argv)
	if res, err := m.db.Exec(query, argv...); err != nil {
		return err
	} else if id, err := res.LastInsertId(); err != nil {
		return err
	} else {
		fp.SetFieldValue(st.PrimaryKey(), int(id))
	}
	return nil
}

func (m *SimpleModel) Delete(st SimpleTableDefiner) (int, error) {
	pk := st.PrimaryKey()
	fp, ok := st.(TableFieldInfoProvider)
	if !ok {
		fp = ReflectFieldInfo{}.New(st)
	}
	id := fp.GetFieldValue(pk).(int)
	query := "DELETE FROM `" + st.TableName() + "` WHERE `" + fp.ColumnName(pk) + "` = ?"
	fmt.Println(query, id)
	if res, err := m.db.Exec(query, id); err != nil {
		return 0, err
	} else if count, err := res.RowsAffected(); err != nil {
		return 0, err
	} else {
		return int(count), nil
	}
}

func (m *SimpleModel) Update(st SimpleTableDefiner) (int, error) {
	pk := st.PrimaryKey()
	fp, ok := st.(TableFieldInfoProvider)
	if !ok {
		fp = ReflectFieldInfo{}.New(st)
	}
	id := fp.GetFieldValue(pk).(int)
	fs := fp.Fields()
	nfs := make([]string, 0)
	for _, f := range fs {
		if f != pk {
			nfs = append(nfs, f)
		}
	}
	fs = nfs
	if len(fs) == 0 {
		return 0, errors.New("要更新的数据行数为空")
	}
	cs := make([]string, 0)
	for _, f := range fs {
		cs = append(cs, fp.ColumnName(f))
	}
	query := "UPDATE `" + st.TableName() + "` SET " + strings.Join(cs, " = ?, ") + " =? WHERE `" + fp.ColumnName(pk) + "` = ?"
	argv := make([]interface{}, 0)
	for _, f := range fs {
		argv = append(argv, fp.GetFieldValue(f))
	}
	argv = append(argv, id)
	fmt.Println(query, argv)
	if res, err := m.db.Exec(query, argv...); err != nil {
		return 0, err
	} else if count, err := res.RowsAffected(); err != nil {
		return 0, err
	} else {
		return int(count), nil
	}
}

// 从数据库中同步数据
func (m *SimpleModel) Load(st SimpleTableDefiner) error {
	if reflect.TypeOf(st).Kind() != reflect.Ptr {
		panic("SimpleModel.New 接收的Table接口必须是指针类型")
	}
	pk := st.PrimaryKey()
	fp, ok := st.(TableFieldInfoProvider)
	if !ok {
		fp = ReflectFieldInfo{}.New(st)
	}
	id := fp.GetFieldValue(pk).(int)
	fs := fp.Fields()
	nfs := make([]string, 0)
	for _, f := range fs {
		if f != pk {
			nfs = append(nfs, f)
		}
	}
	fs = nfs
	if len(fs) == 0 {
		return errors.New("要查询的数据行数为空")
	}
	cs := make([]string, 0)
	for _, f := range fs {
		cs = append(cs, fp.ColumnName(f))
	}
	query := "SELECT `" + strings.Join(cs, "`, `") + "` FROM `" + st.TableName() + "` WHERE `" + fp.ColumnName(pk) + "` = ?"
	fmt.Println(query, id)
	if err := m.db.Get(st, query, id); err != nil {
		return err
	}
	return nil
}

// 查询指定id的一条数据
// 返回的数据是指向结构体的指针
func (m *SimpleModel) Get(st SimpleTableDefiner, id int) (SimpleTableDefiner, error) {
	pk := st.PrimaryKey()
	fp, ok := st.(TableFieldInfoProvider)
	if !ok {
		fp = ReflectFieldInfo{}.New(st)
	}
	fs := fp.Fields()
	if len(fs) == 0 {
		return nil, errors.New("要查询的数据行数为空")
	}
	cs := make([]string, 0)
	for _, f := range fs {
		c := fp.ColumnName(f)
		cs = append(cs, c)
	}
	query := "SELECT `" + strings.Join(cs, "`, `") + "` FROM `" + st.TableName() + "` WHERE `" + fp.ColumnName(pk) + "` = ?"

	dt := reflect.TypeOf(st)
	if dt.Kind() == reflect.Ptr {
		dt = dt.Elem()
	}
	p := reflect.New(dt).Interface().(SimpleTableDefiner)
	fmt.Println(query, id)
	if err := m.db.Get(p, query, id); err != nil {
		return nil, err
	}
	if _, ok := st.(Agency); ok {
		BindAgent(m.db, p)
	}
	return p, nil
}
